package com.shuwen.cdp.track.service.impl;

import com.shuwen.cdp.track.constants.CacheKeys;
import com.shuwen.cdp.track.constants.ConfigServerAddressConstants;
import com.shuwen.cdp.track.entity.BaseLibEntity;
import com.shuwen.cdp.track.entity.BaseUserEntity;
import com.shuwen.cdp.track.entity.DataEntity;
import com.shuwen.cdp.track.mapper.EventMapper;
import com.shuwen.cdp.track.mapper.UserInfoMapper;
import com.shuwen.cdp.track.service.UserLogService;
import com.shuwen.cdp.track.utils.CacheSingletonUtil;
import com.shuwen.cdp.track.utils.IPUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.UUID;

@Service
public  class UserLogServiceImpl implements UserLogService {

    @Resource
    UserInfoMapper userInfoMapper;

    @Resource
    EventMapper eventMapper;

    public String insertUser(String first_id, String second_id, String device_id_list, String project) {

        try{
            BaseUserEntity result = userInfoMapper.selectUserByFirstId(first_id, project);
            if(result != null){
                return result.getUser_id();
            }

            //根据设备ID查埋点ID，查不到将插入一条新设备在user表中
            if(StringUtils.isBlank(second_id)){
                second_id = "''";
            }
            if(StringUtils.isBlank(device_id_list)){
                device_id_list = "''";
            }
            String columns = "user_id,first_id,second_id,device_id_list,project";
            StringBuilder values = new StringBuilder();
            String userId = UUID.randomUUID().toString();

            values.append("'").append(userId).append("',")
                    .append(first_id).append(",")
                    .append(second_id).append(",")
                    .append(device_id_list).append(",")
                    .append(project);

            userInfoMapper.insertUserInfo(columns, values.toString());
            return userId;

        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    //根据设备ID查询用户
    private BaseUserEntity selectUserByFirstId(String firstId, String project){
        String key = CacheKeys.getFirstIdByKey(project,firstId);
        Object cacheData = CacheSingletonUtil.getInstance().getCacheData(key);
        if(cacheData !=null){
            return (BaseUserEntity) cacheData;
        }
        try {
            BaseUserEntity result = userInfoMapper.selectUserByFirstId(firstId, project);

            if(result != null){
                CacheSingletonUtil.getInstance().addCacheData(key,result);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //根据业务系统的用户ID查询用户信息
    private BaseUserEntity selectUserBySecondId(String secondId,String project){
        try {
            if(secondId==null){
                return null;
            }
            String key = CacheKeys.getSecondIdByKey(project,secondId);
            Object cacheData = CacheSingletonUtil.getInstance().getCacheData(key);
            if(cacheData!=null){
                return (BaseUserEntity) cacheData;
            }
            BaseUserEntity result = userInfoMapper.selectUserBySecondId(secondId, project);

            if(result != null){
                CacheSingletonUtil.getInstance().addCacheData(key,result);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //根据userid更新用户信息
    private boolean updateUserByUserId(BaseUserEntity baseUserEntity){
        try{
            StringBuilder columns = new StringBuilder();
            columns.append("first_id='").append(baseUserEntity.getFirst_id()).append("',")
                    .append("second_id='").append(baseUserEntity.getSecond_id()).append("'").append("',")
                    .append("device_id_list='").append(baseUserEntity.getDevice_id_list()).append("'");
            StringBuilder where = new StringBuilder();
            where.append("user_id = '").append(baseUserEntity.getUser_id()).append("'");
            userInfoMapper.updateUserByInfo(columns.toString(), where.toString());
            if(StringUtils.isNotBlank(baseUserEntity.getFirst_id())){
                String key = CacheKeys.getFirstIdByKey(baseUserEntity.getProject(),baseUserEntity.getFirst_id());
                CacheSingletonUtil.getInstance().addCacheData(key,baseUserEntity);
            }
            if(StringUtils.isNotBlank(baseUserEntity.getSecond_id())){
                String key = CacheKeys.getSecondIdByKey(baseUserEntity.getUser_id(),baseUserEntity.getSecond_id());
                CacheSingletonUtil.getInstance().addCacheData(key,baseUserEntity);
            }

            return true;
        }catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    //用户多端数据操作，数据修复
    private void userTerminalDataRepair(String original_id,String user_id,String project){
        try{
            BaseUserEntity baseUserEntity = selectUserByFirstId(original_id, project);
            if(baseUserEntity!=null && StringUtils.isBlank(baseUserEntity.getSecond_id())){

                StringBuilder columns = new StringBuilder();
                columns.append("user_id='").append(user_id).append("'");
                StringBuilder where = new StringBuilder();
                where.append("user_id = '").append(user_id).append("' and ")
                        .append("project = ").append(project);
                eventMapper.updateEvents(columns.toString(), where.toString());

                columns = new StringBuilder();
                columns.append("status='1'");
                where = new StringBuilder();
                where.append("user_id = '").append(baseUserEntity.getUser_id()).append("'");
                userInfoMapper.updateUserByInfo(columns.toString(), where.toString());

                String key = CacheKeys.getFirstIdByKey(project,baseUserEntity.getFirst_id());
                CacheSingletonUtil.getInstance().removeCacheData(key);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //处理用户
    public void userCallBack(DataEntity value){
        String project = value.getProject();
        if(StringUtils.isNotBlank(project) && StringUtils.isNotBlank(value.getType())){
            String userId = "";
            String login_id = "";
            //调用登录时，会给此字段，代表登录前的设备ID
            String original_id = value.getOriginal_id();
            //登录前为设备ID，登录后为用户业务系统ID
            String distinctId = value.getDistinct_id();
            //登录处理
            if(StringUtils.equals(value.getType(),"track_signup")){
                login_id = distinctId;
                BaseUserEntity baseUserEntity = selectUserBySecondId(login_id,project);
                if(baseUserEntity!=null){
                    //根据登录ID查到用户，使用此用户的埋点系统ID，且更新此用户对应的设备列表
                    if(!baseUserEntity.getDevice_id_list().contains(original_id)){
                        baseUserEntity.setDevice_id_list(baseUserEntity.getDevice_id_list()+","+original_id);
                        updateUserByUserId(baseUserEntity);
                        //修复数据，神策用户标识，方案3
                        //例如A用户在一台手机设备ID为X，并进行一系列操作，生成Userid，为1，设备IDX，某一天登录了，second_id为A。user表数据 userid:1 distinct_id:X second_id:A
                        //该用户更换了一个新的苹果手机，并进行一系列操作，由于尚未登录，此时神策使用全新的设备 ID Y 来标识此设备，发送的 distinct_id 为 Y，对应分配的神策 ID 为 2，
                        // 若客户端 SDK 调用接口 profile_set ，则将神策 ID 2 、设备 ID Y 存入 users 表的 id, first_id 字段。
                        //该用户在苹果手机上使用账号 A 进行登录，此时神策将设备 ID Y 与登录 ID A 进行关联，关联成功，对应的神策 ID 依然为 1，同时将设备 ID Y 添加到 users 表中神策 ID 1 的 device_id_list 字段。
                        //该用户登录之后的后续操作，发送的 distinct_id 为 A，所以仍以神策 ID 1 标识
                        //todo 修复方案
                        //修复逻辑，根据当前登录人的original_id(登录前的设备ID)，查找用户，如果userid不等于当前userid说明需要修复
                        //将此前original_id对应userid的数据全部更新至该用户ID上。
                        userTerminalDataRepair(original_id,baseUserEntity.getUser_id(),project);
                    }
                    userId = baseUserEntity.getUser_id();
                }else{
                    //未找到该用户，根据设备ID寻找
                    BaseUserEntity userExistByFirstId = selectUserByFirstId(original_id,project);
                    //根据设备ID找到，用户为空，则将此人与此设备进行绑定，更新操作。
                    if(userExistByFirstId!=null && StringUtils.isBlank(userExistByFirstId.getSecond_id())){
                        userExistByFirstId.setSecond_id(distinctId);
                        userExistByFirstId.setDevice_id_list(original_id);
//                        log.info("开始更新"+value.getEvent()+distinctId);
                        updateUserByUserId(userExistByFirstId);
//                        log.info("更新完成"+value.getEvent()+distinctId);
                        userId = userExistByFirstId.getUser_id();
                    }else{
                        if(userExistByFirstId==null){
                            //正常情况下，不应出现该情况，因测试存在直接
                            userId = insertUser(original_id,distinctId,original_id,project);
                        }else{
                            //根据设备ID找到，但用户ID却不为空说明设备被别的账号绑定，给此人在加一个user记录，使用新的埋点系统ID
                            userId = insertUser(distinctId,distinctId,"''",project);
                        }
                    }
                }
            }else{
                //distinctId有可能是登录ID，故而先根据登录ID查询
                BaseUserEntity baseUserEntity = selectUserBySecondId(distinctId,project);
                if(baseUserEntity == null){
                    //根据设备ID查埋点ID，查不到将插入一条新设备在user表中
                    baseUserEntity = selectUserByFirstId(distinctId,project);
                    if(baseUserEntity!=null){
                        userId = baseUserEntity.getUser_id();
                    }else{
                        userId=insertUser(distinctId,"","",project);
                    }
                }else{
                    userId = baseUserEntity.getUser_id();
                    login_id = baseUserEntity.getSecond_id();
                }
            }
            value.getProperties().setUser_id(userId);
            value.getProperties().set_$is_login_id(login_id);
        }
    }

    @Override
    public void logInsert(DataEntity value){
        if(!ConfigServerAddressConstants.IS_PROD){
            //非正式环境部署，默认给test项目名称，防止数据重复。
            value.setProject("test");
        }
        //用户ID处理
        userCallBack(value);
        try {
            StringBuilder columns = new StringBuilder();
            columns.append("distinct_id,event,$lib,$lib_version,$screen_height,$screen_width,$latest_referrer,$latest_referrer_host,$latest_traffic_source_type,$latest_search_keyword,")
                    .append("$referrer,$referrer_host,$url,$url_path,$title,$is_first_day, $is_first_time,$event_duration,$element_id,$element_content,")
                    .append("$element_name, $element_class_name,$element_type,$element_selector,$element_target_url, $time,server_time,channel,channel_type,$page_x,")
                    .append( "$page_y,commodity_detail_souce, commodity_id,commodity_name,commodity_tag,first_commodity,second_commodity, original_price,present_price,discount_price,")
                    .append("quantity_unit,monetary_unit, store_id,store_name,supplier_id,supplier_name,service_type,get_type, is_success,fail_reason,")
                    .append("account,login_method,is_quick_login,commodity_size, commodity_colour,commodity_quantity,viewpoint_height,entrance,order_id, order_amount,")
                    .append("order_actual_amount,receiver_name,receiver_province, receiver_city,receiver_area,receiver_address,transportation_costs, discount_name,discount_amount,is_use_discount,")
                    .append("if_use_integral, number_of_integral,integral_discount_amount,is_cash_back,cash_back_amount, cash_back_name,commodity_discount_amount,total_price_of_commodity, payment_method,delivery_method," )
                    .append("page_type,banner_belong_area,banner_type, banner_name,banner_id,url,banner_rank,project,platform_type,register_method," )
                    .append("type,anonymous_id,login_id,$app_id,$model,$manufacturer,$os,$os_version,$is_login_id,$longitude," )
                    .append("$latitude,$ip,$timezone_offset,$country,$province,$city,$network_type,$browser,$browser_version,$scene," )
                    .append("$latest_utm_source,$latest_utm_medium,$latest_utm_term,$latest_utm_content,$latest_utm_campaign,$latest_scene,$url_query,$brand,$mp_client_app_version,$mp_client_basic_library_version," )
                    .append("$geo_coordinate_system,$app_version,$wifi,$carrier,$device_id,$app_name,$screen_orientation,user_id,$identity_login_id,flume_time, " )
                    .append("_track_id");
            StringBuilder values = new StringBuilder();
            if(value.getLib() == null){
                value.setLib(new BaseLibEntity());
            }
            String country = "''";
            String province = "''";
            String city = "''";
            if(StringUtils.isNotBlank(value.getIp())){
                try {
                    String cityInfo = IPUtils.getCityInfo(value.getIp());
                    String[] cityInfoArray = cityInfo.split("\\|");
                    country = "'"+cityInfoArray[0]+"'";
                    if(StringUtils.equals(country,"0") || StringUtils.equals(country,"|")){
                        country="''";
                    }
                    province = "'"+cityInfoArray[2]+"'";
                    if(StringUtils.equals(province,"0") || StringUtils.equals(province,"|")){
                        province="''";
                    }
                    city = "'"+cityInfoArray[3]+"'";
                    if(StringUtils.equals(city,"0") || StringUtils.equals(city,"|")){
                        city="''";
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            LocalDateTime.now(ZoneId.of("Asia/Shanghai"));

            Long nowTime = System.currentTimeMillis();
            if(value.getTime()==null){
                value.setTime(nowTime);
            }

            if(value.getFlume_time()==null){
                value.setFlume_time(value.getTime());
            }

            values.append(value.getDistinct_id()).append(",")
                    .append(value.getEvent()).append(",")
                    .append(value.getLib().get_$lib()).append(",")
                    .append(value.getLib().get_$lib_version()).append(",")
                    .append(value.getProperties().get_$screen_height()).append(",")
                    .append(value.getProperties().get_$screen_width()).append(",")
                    .append(value.getProperties().get_$latest_referrer()).append(",")
                    .append(value.getProperties().get_$latest_referrer_host()).append(",")
                    .append(value.getProperties().get_$latest_traffic_source_type()).append(",")
                    .append(value.getProperties().get_$latest_search_keyword()).append(",")
                    .append(value.getProperties().get_$referrer()).append(",")
                    .append(value.getProperties().get_$referrer_host()).append(",")
                    .append(value.getProperties().get_$url()).append(",")
                    .append(value.getProperties().get_$url_path()).append(",")
                    .append(value.getProperties().get_$title()).append(",")
                    .append(value.getProperties().get_$is_first_day()).append(",")
                    .append(value.getProperties().get_$is_first_time()).append(",")
                    .append(value.getProperties().getEvent_duration()).append(",")
                    .append(value.getProperties().get_$element_id()).append(",")
                    .append(value.getProperties().get_$element_content()).append(",")
                    .append(value.getProperties().get_$element_name()).append(",")
                    .append(value.getProperties().get_$element_class_name()).append(",")
                    .append(value.getProperties().get_$element_type()).append(",")
                    .append(value.getProperties().get_$element_selector()).append(",")
                    .append(value.getProperties().get_$element_target_url()).append(",")
                    .append(nowTime).append(",")
                    .append(nowTime).append(",")
                    .append(value.getProperties().getChannel()).append(",")
                    .append(value.getProperties().getChannel_type()).append(",")
                    .append(value.getProperties().get_$page_x()).append(",")
                    .append(value.getProperties().get_$page_y()).append(",")
                    .append(value.getProperties().getCommodity_detail_souce()).append(",")
                    .append(value.getProperties().getCommodity_id()).append(",")
                    .append(value.getProperties().getCommodity_name()).append(",")
                    .append(value.getProperties().getCommodity_tag()).append(",")
                    .append(value.getProperties().getFirst_commodity()).append(",")
                    .append(value.getProperties().getSecond_commodity()).append(",")
                    .append(value.getProperties().getOriginal_price()).append(",")
                    .append(value.getProperties().getPresent_price()).append(",")
                    .append(value.getProperties().getDiscount_price()).append(",")
                    .append(value.getProperties().getQuantity_unit()).append(",")
                    .append(value.getProperties().getMonetary_unit()).append(",")
                    .append(value.getProperties().getStore_id()).append(",")
                    .append(value.getProperties().getStore_name()).append(",")
                    .append(value.getProperties().getSupplier_id()).append(",")
                    .append(value.getProperties().getSupplier_name()).append(",")
                    .append(value.getProperties().getService_type()).append(",")
                    .append(value.getProperties().getGet_type()).append(",")
                    .append(value.getProperties().getIs_success()).append(",")
                    .append(value.getProperties().getFail_reason()).append(",")
                    .append(value.getProperties().getAccount()).append(",")
                    .append(value.getProperties().getLogin_method()).append(",")
                    .append(value.getProperties().getIs_quick_login()).append(",")
                    .append(value.getProperties().getCommodity_size()).append(",")
                    .append(value.getProperties().getCommodity_colour()).append(",")
                    .append(value.getProperties().getCommodity_quantity()).append(",")
                    .append(value.getProperties().getViewpoint_height()).append(",")
                    .append(value.getProperties().getEntrance()).append(",")
                    .append(value.getProperties().getOrder_id()).append(",")
                    .append(value.getProperties().getOrder_amount()).append(",")
                    .append(value.getProperties().getOrder_actual_amount()).append(",")
                    .append(value.getProperties().getReceiver_name()).append(",")
                    .append(value.getProperties().getReceiver_province()).append(",")
                    .append(value.getProperties().getReceiver_city()).append(",")
                    .append(value.getProperties().getReceiver_area()).append(",")
                    .append(value.getProperties().getReceiver_address()).append(",")
                    .append(value.getProperties().getTransportation_costs()).append(",")
                    .append(value.getProperties().getDiscount_name()).append(",")
                    .append(value.getProperties().getDiscount_amount()).append(",")
                    .append(value.getProperties().getIs_use_discount()).append(",")
                    .append(value.getProperties().getIf_use_integral()).append(",")
                    .append(value.getProperties().getNumber_of_integral()).append(",")
                    .append(value.getProperties().getIntegral_discount_amount()).append(",")
                    .append(value.getProperties().getIs_cash_back()).append(",")
                    .append(value.getProperties().getCash_back_amount()).append(",")
                    .append(value.getProperties().getCash_back_name()).append(",")
                    .append(value.getProperties().getCommodity_discount_amount()).append(",")
                    .append(value.getProperties().getTotal_price_of_commodity()).append(",")
                    .append(value.getProperties().getPayment_method()).append(",")
                    .append(value.getProperties().getDelivery_method()).append(",")
                    .append(value.getProperties().getPage_type()).append(",")
                    .append(value.getProperties().getBanner_belong_area()).append(",")
                    .append(value.getProperties().getBanner_type()).append(",")
                    .append(value.getProperties().getBanner_name()).append(",")
                    .append(value.getProperties().getBanner_id()).append(",")
                    .append(value.getProperties().getUrl()).append(",")
                    .append(value.getProperties().getBanner_rank()).append(",")
                    .append(value.getProject()).append(",")
                    .append(value.getProperties().getPlatform_type()).append(",")
                    .append(value.getProperties().getRegister_method()).append(",")
                    .append(value.getType()).append(",")
                    .append(value.getAnonymous_id()).append(",")
                    .append(value.getLogin_id()).append(",")
                    .append(value.getProperties().get_$app_id()).append(",")
                    .append(value.getProperties().get_$model()).append(",")
                    .append(value.getProperties().get_$manufacturer()).append(",")
                    .append(value.getProperties().get_$os()).append(",")
                    .append(value.getProperties().get_$os_version()).append(",")
                    .append(value.getProperties().get_$is_login_id()).append(",")
                    .append(value.getProperties().get_$longitude()).append(",")
                    .append(value.getProperties().get_$latitude()).append(",")
                    .append(value.getIp()).append(",")
                    .append(value.getProperties().get_$timezone_offset()).append(",")
                    .append(country).append(",")
                    .append(province).append(",")
                    .append(city).append(",")
                    .append(value.getProperties().get_$network_type()).append(",")
                    .append(value.getProperties().get_$browser()).append(",")
                    .append(value.getProperties().get_$browser_version()).append(",")
                    .append(value.getProperties().get_$scene()).append(",")
                    .append(value.getProperties().get_$latest_utm_source()).append(",")
                    .append(value.getProperties().get_$latest_utm_medium()).append(",")
                    .append(value.getProperties().get_$latest_utm_term()).append(",")
                    .append(value.getProperties().get_$latest_utm_content()).append(",")
                    .append(value.getProperties().get_$latest_utm_campaign()).append(",")
                    .append(value.getProperties().get_$latest_scene()).append(",")
                    .append(value.getProperties().get_$url_query()).append(",")
                    .append(value.getProperties().get_$brand()).append(",")
                    .append(value.getProperties().get_$mp_client_app_version()).append(",")
                    .append(value.getProperties().get_$mp_client_basic_library_version()).append(",")
                    .append(value.getProperties().get_$geo_coordinate_system()).append(",")
                    .append(value.getProperties().get_$app_version()).append(",")
                    .append(value.getProperties().get_$wifi()).append(",")
                    .append(value.getProperties().get_$carrier()).append(",")
                    .append(value.getProperties().get_$device_id()).append(",")
                    .append(value.getProperties().get_$app_name()).append(",")
                    .append(value.getProperties().get_$screen_orientation()).append(",")
                    .append(value.getProperties().getUser_id()).append(",")
                    .append(value.getIdentities().get_$identity_login_id()).append(",")
                    .append(value.getFlume_time()).append(",")
                    .append(value.get_track_id());

            eventMapper.insertEvents(columns.toString(), values.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

